package gang.org.springframework.framework.annotation;

import gang.org.springframework.framework.bean.*;
import gang.org.springframework.framework.factory.GangConstructorArgumentValues;
import gang.org.springframework.framework.factory.parsing.GangSourceExtractor;
import gang.org.springframework.framework.io.GangResourceLoader;
import gang.org.springframework.framework.metadata.GangAnnotationMetadata;
import gang.org.springframework.framework.metadata.GangStandardClassMetadata;
import gang.org.springframework.framework.support.GangBeanDefinitionRegistry;
import gang.org.springframework.framework.support.GangBeanNameGenerator;
import gang.org.springframework.framework.type.GangMethodMetadata;

import java.util.Set;

/**
 * @author gang.chen
 * @description spring.factories 自动配置类字符转为 BeanDefinition
 * @Date 2021/8/2 9:14
 */
public class GangConfigurationClassBeanDefinitionReader {

    private GangBeanDefinitionRegistry registry;
    private GangSourceExtractor sourceExtractor;
    private GangResourceLoader resourceLoader;
    private Object environment;
    private GangBeanNameGenerator importBeanNameGenerator;
    private Object importRegistry;

    public final GangConditionEvaluator conditionEvaluator;

    public GangConfigurationClassBeanDefinitionReader(GangBeanDefinitionRegistry registry, GangSourceExtractor sourceExtractor, GangResourceLoader resourceLoader, Object environment, GangBeanNameGenerator importBeanNameGenerator, Object importRegistry) {
        this.registry = registry;
        this.sourceExtractor = sourceExtractor;
        this.resourceLoader = resourceLoader;
        this.environment = environment;
        this.importBeanNameGenerator = importBeanNameGenerator;
        this.importRegistry = importRegistry;
        this.conditionEvaluator= new GangConditionEvaluator(registry,null,null);
    }

    public void loadBeanDefinitions(Set<GangConfigurationClass> configurationClasses){
        for (GangConfigurationClass configurationClass : configurationClasses) {
            loadBeanDefinitionsForConfigurationClass(configurationClass,null);
        }
    }

    /**
     * 业务意义
     * @param configClass 对应工厂类，例如：WebMvcAutoConfiguration$EnableWebMvcConfiguration.class
     * */
    public void loadBeanDefinitionsForConfigurationClass(GangConfigurationClass configClass,
                                                         Object trackedConditionEvaluator){

        //TODO 项目主类不做业务处理

        /*
         * 业务意义
         * 创建Bean有两种方式
         * 1）方式一：由类名上的注解引入，例如：@GConfiguration | org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration
         * 2）方式二：由方法上的注解引入，例如：@GBean public GangResourceUrlProvider mvcResourceUrlProvider(){}
         * 该场景处理方式一的Bean的创建， 断点方式configClass.metadata.className = gang.org.springframework.framework.web.servlet.GangWebMvcAutoConfiguration$GangEnableWebMvcConfiguration
         * */
        if(configClass.isImported()){
            registerBeanDefinitionForImportedConfigurationClass(configClass);
        }

        /*
         * 业务意义
         * 1）每一个工厂类自身，或其父类的方法，都可能被@Bean注解，针对引类方法，要生成对应的BeanDefinition类
         * 2）GangBeanMethod 是对类中方法的描述，包含两个属性
         *   2.1 metadata 方法
         *   2.2 GangConfigurationClass 方法返回类
         *   2.3 对应方法为：public GangResourceUrlProvider mvcResourceUrlProvider(){}
         * */
        for (GangBeanMethod beanMethod : configClass.getBeanMethods()) {
            loadBeanDefinitionsForBeanMethod(beanMethod);
        }

    }

    private void registerBeanDefinitionForImportedConfigurationClass(GangConfigurationClass configClass) {
        GangAnnotationMetadata metadata = configClass.getMetadata();
        GangAnnotatedGenericBeanDefinition configBeanDef = new GangAnnotatedGenericBeanDefinition(metadata);
        //TODO
        configBeanDef.setScope("singleton");
        String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);

        GangBeanDefinitionHolder definitionHolder = new GangBeanDefinitionHolder(configBeanDef,configBeanName);
        this.registry.registerBeanDefinition(definitionHolder.getBeanName(),definitionHolder.getBeanDefinition());

        configClass.setBeanName(configBeanName);
    }
    /**
     * TODO 业务意义
     * 1）将类中注解了@Bean的方法，注册为BeanDefinition
     * 2）处理方法：public GangResourceUrlProvider mvcResourceUrlProvider(){}
     * */
    private void loadBeanDefinitionsForBeanMethod(GangBeanMethod beanMethod){

        /*
         * GangConfigurationClass -> GangEnableWebMvcConfiguration
         *
         *
         * */
        GangConfigurationClass configClass = beanMethod.getConfigurationClass();

        /*
         * GangMethodMetadata -> 方法：mvcResourceUrlProvider 类：MvcResourceUrlProvider
         *
         * */
        GangMethodMetadata metadata = beanMethod.getMetadata();

        /*
         * methodName = mvcResourceUrlProvider
         *
         * */
        String methodName = metadata.getMethodName();

        /*
         * 业务意义
         * TODO Do we need to mark the bean as skipped by its condition?
         * 1）唤起getBeanNamesForType
         * */
        this.conditionEvaluator.shouldSkip(metadata, null);

        //TODO Consider name and any aliases

        String beanName = methodName;

        //TODO Register aliases even when overridden

        //TODO Has this effectively been overridden before (e.g. via XML)?

        GangConfigurationClassBeanDefinition beanDef = new GangConfigurationClassBeanDefinition(configClass, metadata, beanName);
        beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));

        if (metadata.isstatic()){
            //TODO static @Bean method
            if (configClass.getMetadata() instanceof GangStandardClassMetadata) {
                //TODO
            }else {
                beanDef.setBeanClass(configClass.getMetadata().getClassName());
            }
            beanDef.setUniqueFactoryMethodName(methodName);
        }else{
            //TODO instance @Bean method
            beanDef.setFactoryBeanName(configClass.getBeanName());
            beanDef.setUniqueFactoryMethodName(methodName);
        }

        //TODO metadata instanceof StandardMethodMetadata

        //TODO autowire

        //TODO autowireCandidate

        //TODO initMethod

        //TODO destroyMethod

        //TODO Replace the original bean definition with the target one, if necessary

        /**
         * 1）BeanDefinition实现类 GangConfigurationClassBeanDefinition
         * */
        GangBeanDefinition beanDefinitionRegister = beanDef;
        this.registry.registerBeanDefinition(beanName, beanDefinitionRegister);
    }

    private static class GangConfigurationClassBeanDefinition extends GangRootBeanDefinition implements GangAnnotatedBeanDefinition
    {

        private final String derivedBeanName;
        private final GangAnnotationMetadata annotationMetadata;
        private final GangMethodMetadata factoryMethodMetadata;

        public GangConfigurationClassBeanDefinition(GangConfigurationClass configurationClass,
                                                    GangMethodMetadata methodMetadata,
                                                    String derivedBeanName)
        {
            this.annotationMetadata = configurationClass.getMetadata();
            this.factoryMethodMetadata = methodMetadata;
            this.derivedBeanName = derivedBeanName;
            setResource(configurationClass.getResource());
            setLenientConstructorResolution(false);
        }

        public GangConfigurationClassBeanDefinition(GangBeanDefinition original,
                                                    GangConfigurationClass configurationClass,
                                                    GangMethodMetadata methodMetadata, String derivedBeanName)
        {
            super(original);
            this.derivedBeanName = derivedBeanName;
            this.annotationMetadata = configurationClass.getMetadata();
            this.factoryMethodMetadata = methodMetadata;
        }

        private GangConfigurationClassBeanDefinition(GangConfigurationClassBeanDefinition original){
            super(original);
            this.annotationMetadata = original.annotationMetadata;
            this.factoryMethodMetadata = original.factoryMethodMetadata;
            this.derivedBeanName = original.derivedBeanName;
        }

        @Override
        public GangAnnotationMetadata getMetadata() {
            return null;
        }

        @Override
        public GangRootBeanDefinition cloneBeanDefinition() {
            return new GangConfigurationClassBeanDefinition(this);
        }

        @Override
        public GangConstructorArgumentValues getConstructorArgumentValues() {
            return null;
        }

        @Override
        public boolean hasConstructorArgumentValues() {
            return super.hasConstructorArgumentValues();
        }
    }


}
